﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Runtime.Serialization.Json;
using System.IO;

namespace GFE.SharePoint.CSharpExtensions
{
    public static class ObjectExtensions
    {
        /// <summary>
        /// 
        /// Workaround for missing "Switch(true) case Var == "test"... " in C#
        /// 
        /// type.Switch(new Func Type, bool>[]
        /// {
        ///     (delegate(Type primType)
        ///          {
        ///             if (primType == typeof(Boolean))
        ///                 arg = Boolean.Parse(value);
        ///              return primType == typeof(Boolean);
        ///          }),
        ///     (delegate(Type primType)
        ///          {
        ///              if (primType == typeof(Int32))
        ///                  arg = Int32.Parse(value);
        ///              return primType == typeof(Int32);
        ///          }),
        ///     (delegate(Type primType)
        ///          {
        ///              if (primType == typeof(Double))
        ///                  arg = Double.Parse(value);
        ///              return primType == typeof(Double);
        ///          }),
        ///     (delegate(Type primType)
        ///          {
        ///              if (primType == typeof(Single))
        ///                  arg = Single.Parse(value);
        ///              return primType == typeof(Single);
        ///          })
        /// });
        /// 
        /// </summary>
        /// <typeparam name="T">Type of argument to test against</typeparam>
        /// <param name="arg">Object to test against</param>
        /// <param name="caseFuncArray">"Case statements" which returns true/false depending on the case statement</param>
        public static void Switch<T>(this T arg, params Func<T, bool>[] caseFuncArray)
        {
            foreach (var caseFunc in caseFuncArray.Where(caseFunc => caseFunc(arg)))
            {
                break;
            }
        }

        /// <summary>
        /// Method for execution of action with no posibility for ending in a exception. If exception occured the errorObject is returned.
        /// </summary>
        /// <typeparam name="T">Type of result</typeparam>
        /// <param name="errorObject">Object indicating that an exception occured</param>
        /// <param name="safeTryCatchAction">Action to be executed under surveillance</param>
        /// <returns></returns>
        public static T SafeTryCatch<T>(T errorObject, Func<T> safeTryCatchAction)
        {
            T result;
            try
            {
                result = safeTryCatchAction();
            }
            catch (Exception)
            {
                return errorObject;
            }
            return result;
        }

        /// <summary>
        /// C# implementation of the VB.Net's With statement
        /// </summary>
        /// <typeparam name="T">Type of object to work on</typeparam>
        /// <param name="withItem">Object to perform with-statement on</param>
        /// <param name="withAction">Action to perfom on with-object</param>
        /// <returns></returns>
        public static T With<T>(this T withItem, Action<T> withAction)
        {
            withAction(withItem);
            return withItem;
        }

        /// <summary>
        /// Generic TryParse. If an error occurs during conversion the default value of the type is returned.
        /// </summary>
        /// <typeparam name="T">Type to convert string value to</typeparam>
        /// <param name="value">String value of object to convert to T</param>
        /// <returns>String value converted to T</returns>
        public static T Parse<T>(string value)
        {
            try { return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value); }
            catch { return default(T); };
        }

        public static T ConvertTo<T>(this string value)
        {
            try { return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value); }
            catch { return default(T); };
        }

        /// <summary>
        /// Generic TryParse. If an error occurs during conversion the default value of the type is returned.
        /// </summary>
        /// <typeparam name="T">Type to convert object value to</typeparam>
        /// <param name="value">Object value of object to convert to T</param>
        /// <returns>Object value converted to T</returns>
        public static T Parse<T>(object value)
        {
            try { return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertTo(value, typeof(T)); }
            catch { return default(T); };
        }

        public static T ConvertTo<T>(this object value)
        {
            return ConvertTo(value, default(T));
        }

        public static T ConvertTo<T>(this object value, T defaultValue)
        {
            if (Equals(value, default(T))) return defaultValue;
            var valueType = value.GetType();
            if (valueType == typeof(T)) return (T)value;
            if (valueType == typeof(String)) return ConvertTo<T>(value as string);
            try
            {
                return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertTo(value, typeof(T));
            }
            catch
            {
                return defaultValue;
            }
        }

        public static TY MapTo<T, TY>(this T mapFrom, Func<T, TY> mapFunc)
        {
            return mapFunc(mapFrom);
        }

        public static IEnumerable<Y> MapListTo<T, Y>(this IEnumerable<T> from, Func<T, Y> mapFunc)
        {
            var toList = new List<Y>();
            foreach (T fromItem in from)
            {
                toList.Add(mapFunc(fromItem));
            }

            return toList;
        }

        public static string JsonSerializeThis<T>(this T objectToSerialize) where T : class
        {
            if (objectToSerialize == null)
            {
                throw new ArgumentException("objectToSerialize must not be null");
            }
            MemoryStream ms = null;
            try
            {
                var serializer = new DataContractJsonSerializer(objectToSerialize.GetType());
                ms = new MemoryStream();
                serializer.WriteObject(ms, objectToSerialize);
                return Encoding.UTF8.GetString(ms.ToArray());
            }
            finally
            {
                if (ms != null)
                {
                    ms.Close();
                }
            }
        }

    }
}
